Alumnos

Grupo 1:

  • Grijalba
  • Richard
  • Ruiz
  • Szekieta

Detalle

En este trabajo, exploraremos diversas técnicas de minería de datos para extraer valor del texto obtenido de una plataforma de monitoreo agrícola.

El trabajo diario de los asesores e ingenieros agrónomos es variado, y una de sus tareas clave es el monitoreo de plagas, malezas y enfermedades. Esta actividad es crucial para abordar los problemas de manera profesional, ya que antes de tomar decisiones clave para la producción, siempre es importante realizar un monitoreo que valida determinadas acciones sobre los lotes como puede ser la aplicacion de algun producto quimico.

Desde sus inicios, el monitoreo se ha realizado de diversas formas. Sin embargo, con la revolución digital que ha cobrado protagonismo en los últimos años, el monitoreo no se ha quedado atrás. Los asesores están adoptando cada vez más plataformas digitales para registrar todas las acciones realizadas. No obstante, algunas "viejas costumbres" persisten. Es común ver a asesores tomando notas de monitoreo a través de audios y notas escritas, como solían hacerlo antes en una libreta.

De esta manera, el monitoreo aún no ha alcanzado una digitalización plena. Muchos de esos audios y notas se pierden, sin aportar información valiosa para la campaña en curso y mucho menos para campañas futuras, sin mencionar la perdida de trazabilidad y valor que estas pordian aportar, aqui estamos hablando directamente de dinero que se esta perdiendo por no registrar de manera correcta los monitoreos.

El dataset se denominará annotations.csv y contendrá las siguientes columnas:

  • annotation_id: ID único de la anotación
  • company_id: ID de la compañía asociada a la anotación
  • season_id: ID de la campaña asociada a la anotación
  • property_id: ID de la propiedad asociada a la anotación
  • area_id: ID del lote asociado a la anotación
  • comments: Anotación en formato texto
  • scouter_id: ID del asesor
  • annotation_date: Fecha de la anotación
  • annotation_tags: Etiquetas o tags indicados por el asesor
  • annotation_longitude: Longitud de la anotación
  • annotation_latitude: Latitud de la anotación

Por ejemplo, al texto comments="este lote tiene rama negra", podríamos etiquetarlo como "Maleza" o "Rama Negra". Esta nueva etiqueta aporta mucho valor a la nota, permitiendo cuantificar a nivel regional los niveles de malezas, plagas y enfermedades, y posibilitando la integración de esta información con otras fuentes de datos para aportar valor al mercado.

Librerías utilizadas

#!python -m spacy download es_core_news_sm
import pandas as pd
import matplotlib.pyplot as plt
import re
from unidecode import unidecode
from wordcloud import WordCloud

pd.set_option('display.max_columns', None)
#pd.set_option('display.max_rows', None)
# Podemos ejecutar la notebook con una muestra aleatoria de 1000 comentarios
# Si es True la notebook es ejecutada con la muestra de comentarios
# Si es False la notebook es ejecutada con todos los comentarios
RUN_WITH_SAMPLE = False

# Definir si vamos a guardar o no las salidas de la notebook
SAVE_NOTEBOOK_RESULTS = False

# Nombre del detaset etiquetado a partir de los comentarios
file_with_tags = "Workflow_output/annotations_processed.csv"

Lectura del dataset

# Importamos archivo csv
annotations = pd.read_csv('../data/annotations.csv')
annotations.head(5)
annotation_id company_id season_id property_id area_id comments scouter_id annotation_date annotation_tags annotation_longitude annotation_latitude
0 01ff3c51-d6c2-4b9a-9876-409e75a824d5 0afb4f42-d63b-45ee-bbf8-df16f94529f7 289c09d7-ce26-4fa9-bf9a-b7cab391898f bd3e8b1b-6914-4e22-9e63-1c0c66ccb148 91420df8-61ba-11ed-9c62-e1a227e78cde Ir hablado con desyuyadores ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-07 13:31:09.234 NaN -62.578112 -33.103599
1 404e3e04-b713-493e-830a-56570f997a14 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 036afeae-1c42-4c93-9a06-2f20977b89ac Maiz recien sembrado . Todo ok ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 10:30:55.251 NaN -62.809891 -33.741697
2 60f06b01-c926-42b3-b79d-5b4b61e2e6fb 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae b24c5fd5-87b2-40dd-97cc-ca4edff7a625 Comienzo siembra maiz tardio. Excelente barbecho ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 10:30:05.531 NaN -62.809891 -33.741697
3 b53c4cac-1a21-45fe-be7c-235b08d704c9 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 1af5d0f7-7a22-4aef-8c10-c65c12708ab3 Hacer preemergente con glifo y acuron ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 12:18:16.170 NaN -62.770486 -33.655338
4 6bbdc37c-4ca4-4082-a443-aedd206cf24e 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 9d6caf86-eeca-4124-a2fb-42394a148771 Hacer preemergente con acuron y heat ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 12:17:43.856 NaN -62.770486 -33.655338

Validaciones

# volumen de comentarios
num_annotations = annotations.shape[0]
null_count = annotations['comments'].isnull().sum()
non_null_count = annotations['comments'].notnull().sum()


print("Cantidad de comentarios:", num_annotations)
print("Comentarios 'Nulos':", null_count)
print("Comentarios 'No nulos':", non_null_count)
Cantidad de comentarios: 26930
Comentarios 'Nulos': 6374
Comentarios 'No nulos': 20556
# distribución de las etiquetas asignadas a los comentarios
annotations['annotation_tags'].value_counts(dropna=False,normalize=True)
annotation_tags
NaN                                                                                                         0.903639
tags_AR_Resumen                                                                                             0.030746
tags_general_weeds                                                                                          0.023468
tags_AR_Resumen,tags_general_weeds                                                                          0.014519
tags_general_other                                                                                          0.010843
tags_general_nutrient                                                                                       0.003305
tags_AR_Rendimiento                                                                                         0.002562
tags_general_pest                                                                                           0.002525
tags_general_disease                                                                                        0.002488
tags_AR_Resumen,tags_general_pest                                                                           0.001225
tags_general_deficiency                                                                                     0.000706
tags_AR_Resumen,tags_general_other                                                                          0.000594
tags_general_disease,tags_general_weeds                                                                     0.000483
tags_AR_Resumen,tags_general_pest,tags_general_weeds                                                        0.000371
[,]                                                                                                         0.000297
tags_general_pest,tags_general_weeds                                                                        0.000297
tags_controle_certo_application                                                                             0.000260
tags_AR_Resumen,tags_general_nutrient                                                                       0.000223
tags_general_other,tags_general_weeds                                                                       0.000186
tags_general_irrigation                                                                                     0.000186
tags_AR_Resumen,tags_general_disease                                                                        0.000111
tags_controle_certo_application,tags_general_weeds                                                          0.000111
tags_AR_Rendimiento,tags_AR_Resumen                                                                         0.000074
tags_general_deficiency,tags_general_disease,tags_general_weeds                                             0.000074
tags_general_deficiency,tags_general_nutrient                                                               0.000074
tags_general_disease,tags_general_other                                                                     0.000074
tags_controle_certo_application,tags_general_deficiency                                                     0.000037
tags_AR_Rendimiento,tags_general_other                                                                      0.000037
tags_general_nutrient,tags_general_weeds                                                                    0.000037
tags_general_other,tags_general_pest,tags_general_weeds                                                     0.000037
tags_general_disease,tags_general_nutrient,tags_general_other                                               0.000037
tags_AR_Resumen,tags_general_other,tags_general_weeds                                                       0.000037
tags_AR_Rendimiento,tags_general_weeds                                                                      0.000037
tags_general_deficiency,tags_general_disease,tags_general_nutrient                                          0.000037
tags_general_irrigation,tags_general_weeds                                                                  0.000037
tags_general_deficiency,tags_general_disease,tags_general_nutrient,tags_general_pest                        0.000037
tags_general_deficiency,tags_general_disease                                                                0.000037
tags_AR_Resumen,tags_general_deficiency                                                                     0.000037
tags_general_deficiency,tags_general_disease,tags_general_other,tags_general_pest                           0.000037
tags_general_disease,tags_general_irrigation,tags_general_nutrient,tags_general_other,tags_general_weeds    0.000037
tags_general_disease,tags_general_pest                                                                      0.000037
Name: proportion, dtype: float64

La mayoría de los comentarios no tienen asignada una etiqueta (representa el 90% de la base).

Este campo podría ser imputado mediante el campo de comentario ('comments').

La etiqueta con mayor volumen es 'tags_general_weeds' (maleza).

# Exploración de comentarios
# Ejemplo de algunos comentarios que fueron asignados a la etiqueta 'maleza' y contienen el sstring 'no'
condicion = (annotations['annotation_tags']=='tags_general_weeds') & (annotations['comments'].str.contains('no'))
annotations[condicion].head()
annotation_id company_id season_id property_id area_id comments scouter_id annotation_date annotation_tags annotation_longitude annotation_latitude
6740 cde0f3a4-fc60-4e23-b0eb-dd5cc23e18bf f203a888-9d2a-4a07-a8f5-7d5da9e27aac 7722b6a9-2215-4362-9a79-f47f4e73b83c 8cde247e-5b7d-4c21-8cea-c803352ac63d 004f4288-ab55-4f08-bafd-8147d30fb1ee 28.04.2023 lote cercano a aplicación para barb... ebb3b3f8-d91b-4efa-b838-cca6ea83e360 2023-04-28 11:48:16.468 tags_general_weeds -63.193558 -32.786015
7186 8dbc4ae1-19d2-4113-aaa2-7245344ae16b 6fda311b-bec7-4368-8a57-99f894a2c614 e4183986-0517-4119-bc55-97d62855e25b 4a6a1679-ba40-48ba-8616-084d7c8a423d b11a4a51-7f24-4e73-8c7b-a11654a287ae Rodal de ocucha que no controlo bien 714f8437-b26d-40ef-a441-32f77ade2a3e 2023-06-14 14:25:25.000 tags_general_weeds -63.926227 -32.800571
7255 178b1c3f-df3d-40f7-9987-e2655a189475 e8b4d4a7-825e-49e4-94c5-5730fa5ad33e 7b81b4ee-957a-4939-b7d0-e87d31f4fbda afa3e6e5-af63-4c40-b341-efe6ce394787 482466a5-090f-417d-890f-69e5edadd97f un poco de rye grass y un poco de nabo, no mucho 54cd9c8c-0799-449c-9a22-d9f75c5dd173 2023-06-13 10:43:10.000 tags_general_weeds -61.398361 -38.738875
7256 e0f4b4bb-41db-44d3-900f-9d4becf93d06 e8b4d4a7-825e-49e4-94c5-5730fa5ad33e db99aee4-7ae9-42d7-a4b5-31d2abc81e86 8afecdb7-d5c5-40fc-872f-b77c10810709 b356cf5a-d1bb-4218-9eea-25a23e383850 Rye grass afectado pero todavía no está seco 54cd9c8c-0799-449c-9a22-d9f75c5dd173 2023-06-14 14:13:30.000 tags_general_weeds -61.016213 -38.803795
7276 4ddda2c1-8754-4f14-a20c-12e330e4136d 33f2dcae-049a-4b93-83f3-a8014ea7f0a0 642ff575-39ee-4f7e-b4bc-8f4080f417b7 0f806757-e61d-4f67-81d0-0e71d97cb668 08867a7a-d869-11ed-98c6-150405b698ac Manos grandes a076a950-0b62-4771-bf27-c952163fab2f 2023-06-22 12:24:42.000 tags_general_weeds -59.098272 -37.575422

Procesamiento de los comentarios - Limpieza del dataset

# Extraemos una muestra de la basepara agilizar ejecución de la notebook
# Posteriormente tomaremos la base completa
annotations_full = annotations.copy()

# Tomamos muestra aleatoria de 1000 comentarios para testear su ejecución
if RUN_WITH_SAMPLE:
    annotations = annotations.sample(1000, random_state=42)
# Eliminamos comentarios nulos
annotations = annotations.dropna(subset=['comments'])

Primera limpieza

Realizamos una primera limpieza de los comentarios:

  • Llevar texto a minúscula.
  • Eliminar tíldes y caracteres especiales.
  • Eliminar espacios en blanco al inicio y fin del comentario.
  • Eliminar cualquier carácter que no sea letra, número, espacio o /.
  • Elimina múltiples espacios en blanco.
# Definimos función para limpieza de texto
def limpiar_texto(texto):
    # Convierte el texto a minúsculas
    texto = str(texto)
    texto = texto.lower()
    # Elimina tildes y caracteres especiales
    texto = unidecode(texto)
    # Elimina espacios en blanco al inicio y al final
    texto = texto.strip()
    # Elimina signos de puntuación y otros caracteres no deseados
    texto = re.sub(r'[^a-zA-Z0-9\s/]', '', texto)
    # Elimina múltiples espacios en blanco y los convierte en uno solo
    texto = re.sub(r'\s+', ' ', texto)
    # Devuelve el texto filtrado
    return texto

# Aplicamos limpieza del texto - No pisamos campo original sino que creamos uno nuevo
annotations['comments_clean'] = annotations['comments'].apply(limpiar_texto)
# Comparamos el comentario original con el 'limpio'
annotations[['comments', 'comments_clean']].sample(10, random_state=42)
comments comments_clean
8947 altamisa altamisa
26674 AU au
12078 Este lote esta siendo pastoreado 17-8 este lote esta siendo pastoreado 178
21666 Si bien hay sectores impactados con menor desa... si bien hay sectores impactados con menor desa...
292 Lote limpio. En cabeceras Digitaria aislada y ... lote limpio en cabeceras digitaria aislada y a...
13008 trigo grano pastoso, mucho grano vano trigo grano pastoso mucho grano vano
25035 Lote en etapa de implantación del cultivo, eme... lote en etapa de implantacion del cultivo emer...
23389 Buena apariencia general del cultivo. El trata... buena apariencia general del cultivo el tratam...
9679 un bajo con parietaria un bajo con parietaria
11077 El bajo hay que hacerlo con Glufosinato por Ce... el bajo hay que hacerlo con glufosinato por ce...
# Cantidad de caracteres de los comentarios limpios
annotations["len_comments_clean"] = annotations["comments_clean"].str.len()

# Check comportamiento
annotations["len_comments_clean"].value_counts(dropna=False)
len_comments_clean
7      738
0      479
18     430
22     359
17     334
      ... 
851      1
797      1
957      1
383      1
412      1
Name: count, Length: 444, dtype: int64

Luego de una primera limpieza, podemos identificar comentarios vacíos.

Eliminamos estos comentarios.

# Limpiamos comentarios sin caracteres
annotations = annotations[annotations["len_comments_clean"]>0].reset_index(drop=True).copy()

Segunda limpieza

Además de realizar la 'primera limpieza' mencionada anteriormente, se lleva a cabo una eliminación adicional de 'stopwords' para mejorar la calidad del texto. Esto ayuda a reducir ruido en los datos al eliminar palabras comunes y sin valor predictivo, como 'de', 'la', y 'el', optimizando así el contenido para su análisis.

# TODO: mover importacion de librerias
import re
from unidecode import unidecode
import spacy
import nltk
from nltk.corpus import stopwords

# Cargar modelo en español de spaCy
nlp = spacy.load('es_core_news_sm')

# Descargar stop words de nltk en español
nltk.download('stopwords')

# Crear lista personalizada de stop words
custom_stop_words = set(unidecode(texto) for texto in stopwords.words('spanish')) - {'no', 'sin', 'con', 'pero', 'hay'}

# Tokenizar y eliminar stop words personalizadas usando spaCy
def limpiar_texto(texto):
    # Convierte el texto a minúsculas
    texto = str(texto).lower()
    # Elimina tildes y caracteres especiales
    texto = unidecode(texto)
    # Elimina espacios en blanco al inicio y al final
    texto = texto.strip()
    # Elimina signos de puntuación y otros caracteres no deseados
    texto = re.sub(r'[^a-zA-Z0-9\s/]', '', texto)
    # Elimina múltiples espacios en blanco y los convierte en uno solo
    texto = re.sub(r'\s+', ' ', texto)
    
    # Procesar el texto con spaCy
    doc = nlp(texto)
    
    # Filtrar palabras irrelevantes (stop words) y solo conservar las alfabéticas
    palabras_filtradas = [token.text for token in doc if token.text not in custom_stop_words and token.is_alpha]
    
    # Devuelve el texto filtrado
    return ' '.join(palabras_filtradas)


# Aplicar la función limpiar_texto a la columna 'comments' y crear la columna 'comments_clean'
annotations['comments_clean_2'] = annotations['comments'].apply(limpiar_texto)
# Cantidad de caracteres de los comentarios limpios 2
annotations["len_comments_clean_2"] = annotations["comments_clean_2"].str.len()

# Limpiamos comentarios sin caracteres
annotations = annotations[annotations["len_comments_clean_2"]>0].reset_index(drop=True).copy()
annotations[['comments', 'comments_clean','comments_clean_2']].sample(10)
comments comments_clean comments_clean_2
12989 soja wacha soja wacha soja wacha
14981 a pesar del lote de girasol más apedrado, lleg... a pesar del lote de girasol mas apedrado llego... pesar lote girasol apedrado llego buen iaf cri...
1003 Cultivo cultivo cultivo
11952 Cultivo limpio.\r\neleusine y algo de colorado... cultivo limpio eleusine y algo de colorado en ... cultivo limpio eleusine colorado cabeceras pre...
7373 nabos controlados, cebada controlada, algunos ... nabos controlados cebada controlada algunos ra... nabos controlados cebada controlada raigras ce...
3092 Estado general muy bueno.\r\nLote limpio con p... estado general muy bueno lote limpio con plant... general bueno lote limpio con plantas maiz gua...
17117 Buen estado en general buen estado en general buen general
16294 Daño de arañuela, no hay insectos vivos. dano de aranuela no hay insectos vivos dano aranuela no hay insectos vivos
15041 Lote de buena apariencia, con nacimientos de o... lote de buena apariencia con nacimientos de or... lote buena apariencia con nacimientos orugas t...
9588 36,6 grano/espiga 366 grano/espiga grano espiga

Exploración del texto

Nube de palabras de los comentarios 'limpios' (con stopwords)

# A partir de los comentarios de la 'primera limpieza'

# Crear el objeto WordCloud 
all_comments = ' '.join(annotations['comments_clean'])
wordcloud = WordCloud(width=800, height=400, background_color='white').generate(all_comments)

# Mostrar nube de palabras
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()

Nube de palabras de los comentarios 'limpios' (sin stopwords)

# A partir de los comentarios de la 'segunda limpieza' (sin stopwords)

# Crear el objeto WordCloud 
all_comments = ' '.join(annotations['comments_clean_2'])
wordcloud = WordCloud(width=800, height=400, background_color='white').generate(all_comments)

# Mostrar nube de palabras
plt.figure(figsize=(10, 6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()

Generar bigramas (pares de palabras) y trigramas (tripletas de palabras)

from nltk.util import ngrams
from nltk import FreqDist

# Tokenizar y filtrar palabras irrelevantes usando spaCy
def tokenize(text):
    doc = nlp(text)
    return [token.text for token in doc]

comments = annotations['comments_clean_2'].tolist()
filtered_words = []
for comment in comments:
    words = tokenize(comment)  # tokenizar
    filtered_words.extend(words)
    
    
# Generar bigramas y trigramas
bigrams = ngrams(filtered_words, 2)
trigrams = ngrams(filtered_words, 3)

# Contar frecuencia de bigramas y trigramas
bigram_freq = FreqDist(bigrams)
trigram_freq = FreqDist(trigrams)

Nube de palabras a partir de los bigramas y trigramas

# Crear un diccionario de palabras compuestas con sus frecuencias
composite_words = {f"{k[0]} {k[1]}": v for k, v in bigram_freq.items()}
composite_words.update({f"{k[0]} {k[1]} {k[2]}": v for k, v in trigram_freq.items()})

# Crear una nube de palabras
wordcloud = WordCloud(width=800, height=400, background_color='white').generate_from_frequencies(composite_words)

# Mostrar la nube de palabras
plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()

Tabla frecuencia de los bigramas

# Convertir bigram_freq a un DataFrame de pandas
bigram_data = pd.DataFrame(bigram_freq.items(), columns=['Bigram', 'Frequency'])

# Expandir los bigramas en dos columnas separadas
bigram_data[['Bigram1', 'Bigram2']] = pd.DataFrame(bigram_data['Bigram'].tolist(), index=bigram_data.index)

# Eliminar la columna original del bigrama
bigram_data = bigram_data.drop(columns=['Bigram'])

# Mostrar top 15
bigram_data.sort_values("Frequency", ascending=False).head(15)
Frequency Bigram1 Bigram2
13615 1194 sin inconvenientes
13650 944 buena apariencia
350 857 cultivo con
87 700 rama negra
136 656 lote limpio
14780 557 inconvenientes adversidades
64 450 maiz guacho
109 436 buen control
1156 420 con buena
15597 402 apariencia general
2312 357 lote buena
357 352 lote con
1680 348 buen general
760 341 general cultivo
519 318 yuyo colorado

Tabla frecuencia de los trigramas

# Convertir trigram_freq a un DataFrame de pandas
trigram_data = pd.DataFrame(trigram_freq.items(), columns=['Trigram', 'Frequency'])

# Expandir los trigramas en tres columnas separadas
trigram_data[['Bigram1', 'Bigram2', 'Bigram3']] = pd.DataFrame(trigram_data['Trigram'].tolist(), index=trigram_data.index)

# Eliminar la columna original del trigrama
trigram_data = trigram_data.drop(columns=['Trigram'])

# Mostrar top 15
trigram_data.sort_values("Frequency", ascending=False).head(15)
Frequency Bigram1 Bigram2 Bigram3
22593 544 sin inconvenientes adversidades
37677 379 con buena apariencia
1593 343 cultivo con buena
35427 340 lote buena apariencia
23804 325 buena apariencia general
28739 271 inconvenientes adversidades cultivo
13741 247 buen general cultivo
392 236 cultivo con buen
46879 188 adversidades cultivo con
30508 171 lote sin inconvenientes
49416 153 cultivo con apariencia
48042 129 con buen aspecto
37675 128 sin inconvenientes cultivo
36650 122 buena apariencia con
6303 118 cultivo cultivo cultivo

Generar diccionario para imputar 'tag' del comentario

# Diccionario indicadores
indicadores = pd.read_csv('../data/id_indicadores.csv')
selected_columns = ['phenomenon_name', 'phenomenon_scientific_name', 'phenomenon_category_name']
indicadores = indicadores[selected_columns]

# Primeros registros
indicadores.head()
phenomenon_name phenomenon_scientific_name phenomenon_category_name
0 Flor de la noche Oenothera indecora Malezas
1 Mancha purpura Cercospora kikuchii Enfermedades
2 Chilca Baccharis salicifolia Malezas
3 Ácaros (Araña roja) Tetranychus urticae Plagas
4 Boevaria Boerhavia difussa Malezas
# Diccionario productos
productos = pd.read_excel('../data/id_productos.xlsx')
selected_columns = ['PRODUCTO', 'PRINCIPIO_ACTIVO', 'GRUPO']
productos = productos[selected_columns]

# Primeros registros
productos.head()
PRODUCTO PRINCIPIO_ACTIVO GRUPO
0 AMISTAR AZOXISTROBINA FUNGICIDA
1 AMISTAR 50 WG AZOXISTROBINA FUNGICIDA
2 AMISTAR 50 WG AZOXISTROBINA FUNGICIDA
3 QUADRIS AZOXISTROBINA FUNGICIDA
4 QUADRIS 50 WG AZOXISTROBINA FUNGICIDA
# Aplicamos la misma limpieza a los diccionarios como hicimos con los comentarios
indicadores['phenomenon_name'] = indicadores['phenomenon_name'].apply(limpiar_texto)
indicadores['phenomenon_category_name'] = indicadores['phenomenon_category_name'].apply(limpiar_texto)
indicadores['phenomenon_scientific_name'] = indicadores['phenomenon_scientific_name'].apply(limpiar_texto)

productos['PRODUCTO'] = productos['PRODUCTO'].apply(limpiar_texto)
productos['PRINCIPIO_ACTIVO'] = productos['PRINCIPIO_ACTIVO'].apply(limpiar_texto)
productos['GRUPO'] = productos['GRUPO'].apply(limpiar_texto)
# Crear un nuevo DataFrame con las columnas 'phrase' y 'tag' a partir de los DataFrames 'indicadores' y 'productos'
df_indicadores = pd.DataFrame({
    'phrase': pd.concat([indicadores['phenomenon_name'], indicadores['phenomenon_scientific_name']]),
    'tag': pd.concat([indicadores['phenomenon_category_name'], indicadores['phenomenon_category_name']])
})

df_productos = pd.DataFrame({
    'phrase': pd.concat([productos['PRODUCTO'], productos['PRINCIPIO_ACTIVO']]),
    'tag': pd.concat([productos['GRUPO'], productos['GRUPO']])
})

# Unir ambos DataFrames
df_combined = pd.concat([df_indicadores, df_productos], ignore_index=True)

# Mostrar el nuevo DataFrame
df_combined.head()
phrase tag
0 flor noche malezas
1 mancha purpura enfermedades
2 chilca malezas
3 acaros arana roja plagas
4 boevaria malezas
df_combined[df_combined['phrase'].str.contains('maleza')]
phrase tag
# incluimos el tag como phrase

# Duplicar las filas y cambiar los valores en la columna 'phrase' en las filas duplicadas
df_duplicado = df_combined.copy()
df_duplicado['phrase'] = df_duplicado['tag']
df_duplicado = df_duplicado.drop_duplicates().reset_index(drop=True)

# Concatenar el DataFrame original con el duplicado
df_combined = pd.concat([df_combined, df_duplicado], ignore_index=True)
df_combined[df_combined['phrase'].str.contains('maleza')]
phrase tag
18164 malezas malezas
18174 coberturas surcos malezas coberturas surcos malezas
df_combined["tag"].value_counts()
tag
herbicida                          6009
malezas                            5837
fungicida                          2365
insecticida                        1605
calidad siembra                     763
enfermedades                        243
plagas                              235
herbicidadefoliantedesecante        175
plantas daninhas                    161
fungicida insecticida               149
acaricidainsecticida                115
cosecha                              73
insecticidaacaricidagorgojicida      59
analisis suelo                       49
unkrauter                            41
broad leaved weeds                   41
malas hierbas                        41
stand plantas                        37
insecticida nematicida               35
stand                                31
fungicidaacaricida                   23
coadyuvanteinsecticidaacaricida      17
fitorregulador                       15
defoliante                           15
insetos                              11
general lote                          9
coberturas surcos malezas             9
contenido humedad suelo               9
fertilizante organico                 7
coadyuvante                           5
fugicida                              3
plaga                                 3
fungida                               3
fertilizante biologico                3
antidoto herbicida                    3
Name: count, dtype: int64

Comparar bigrama con diccionario a partir de la distancia euclidea y similaridad del coseno

# euclidean distances
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import euclidean_distances

# Top 1000 bigramas
top_bigrams = (bigram_data.sort_values(by='Frequency', ascending=False)
                          .head(1000)['Bigram1'] + ' ' + bigram_data.sort_values(by='Frequency', ascending=False)
                          .head(1000)['Bigram2']).tolist()

# Combinamos texto
all_text = top_bigrams + df_combined['phrase'].tolist()

# Vectorizamos texto
vectorizer = TfidfVectorizer().fit(all_text)
bigram_vectors = vectorizer.transform(top_bigrams)
phrase_vectors = vectorizer.transform(df_combined['phrase'])

# Calculamos distancia
distance_matrix = euclidean_distances(bigram_vectors, phrase_vectors)

# Asignamos tags en base a las distanicas
tags = []
scores = []
phrases = []
for i, bigram in enumerate(top_bigrams):
    # Obtenemos indice de la menor distancia
    most_similar_index = distance_matrix[i].argmin()
    # Obtenemos el tag asociado al indice
    phrase = df_combined.iloc[most_similar_index]['phrase']
    tag = df_combined.iloc[most_similar_index]['tag']
    score = distance_matrix[i][most_similar_index]
    tags.append(tag)
    scores.append(score)
    phrases.append(phrase)

# Creamos dataframe con los resultados
results_df_eucl_dist = pd.DataFrame({
    'Bigram': top_bigrams,
    'Tag': tags,
    'Phrase': phrases,
    'Score': scores
})

# Display the DataFrame
results_df_eucl_dist.sort_values(by='Score').head(100)
Bigram Tag Phrase Score
103 stand plantas stand plantas stand plantas 0.000000
394 m l analisis suelo 0.000000
48 lote general general lote general lote 0.000000
242 mancha marron enfermedades mancha marron 0.000000
648 dual gold herbicida dual gold 0.000000
... ... ... ... ...
473 tratamiento malezas malezas malezas 0.863893
239 nacimientos malezas malezas malezas 0.863893
346 control colorado calidad siembra control siembra 0.870348
345 con control calidad siembra control siembra 0.872601
804 amarilla con enfermedades mancha amarilla 0.878307

100 rows × 4 columns

# similaridad coseno
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# Top 1000 bigramas
top_bigrams = (bigram_data.sort_values(by='Frequency', ascending=False)
                          .head(1000)['Bigram1'] + ' ' + bigram_data.sort_values(by='Frequency', ascending=False)
                          .head(1000)['Bigram2']).tolist()

# Combinamos texto
all_text = top_bigrams + df_combined['phrase'].tolist()

# Vectorizamos texto
vectorizer = TfidfVectorizer().fit(all_text)
bigram_vectors = vectorizer.transform(top_bigrams)
phrase_vectors = vectorizer.transform(df_combined['phrase'])

# Calculamos similaridad coseno
distance_matrix = cosine_similarity(bigram_vectors, phrase_vectors)

# Asignamos tags en base a las distanicas
tags = []
scores = []
phrases = []
for i, bigram in enumerate(top_bigrams):
    # Obtenemos indice de la menor distancia
    most_similar_index = distance_matrix[i].argmax()
    # Obtenemos el tag asociado al indice
    phrase = df_combined.iloc[most_similar_index]['phrase']
    tag = df_combined.iloc[most_similar_index]['tag']
    score = distance_matrix[i][most_similar_index]
    tags.append(tag)
    scores.append(score)
    phrases.append(phrase)

# Creamos dataframe con los resultados
results_df_coseno_sim = pd.DataFrame({
    'Bigram': top_bigrams,
    'Tag': tags,
    'Phrase': phrases,
    'Score': scores
})

# Display the DataFrame
results_df_coseno_sim.sort_values(by='Score', ascending=False).head(100)
Bigram Tag Phrase Score
242 mancha marron enfermedades mancha marron 1.000000
944 escoba dura malezas escoba dura 1.000000
6 maiz guacho malezas maiz guacho 1.000000
289 pasto bandera malezas pasto bandera 1.000000
123 ortiga mansa malezas ortiga mansa 1.000000
... ... ... ... ...
804 amarilla con enfermedades mancha amarilla 0.614289
899 control lote calidad siembra control siembra 0.612826
450 malezas hoja malezas malezas 0.612707
528 siembra maiz malezas maiz guacho 0.610937
672 viendo cosecha cosecha cosecha 0.609165

100 rows × 4 columns

Comparar bigrama con diccionario a partir de la distancia de Levenshtein

#Distancia de Levenshtein
import pandas as pd
import numpy as np
from Levenshtein import distance as levenshtein_distance

# Armamos lista de bigramas - TOP BIGRAMAS?
bigrams_list = (bigram_data.sort_values(by='Frequency', ascending=False)['Bigram1'] + ' ' + bigram_data.sort_values(by='Frequency', ascending=False)['Bigram2']).tolist()

# Definimos función para calcular la distancia de Levenshtein entre dos listas de strings
def levenshtein_distances(list1, list2):
    distances = np.zeros((len(list1), len(list2)))
    for i, s1 in enumerate(list1):
        for j, s2 in enumerate(list2):
            distances[i, j] = levenshtein_distance(s1, s2) / len(s1)
    return distances

# Calculamos distancias de Levenshtein
distance_matrix = levenshtein_distances(bigrams_list, df_combined['phrase'])

# Asignamos el 'tag' del diccionario a partir de 'phrase'
tags = []
scores = []
phrases = []
for i, bigram in enumerate(bigrams_list):
    # Index 'phrase' con menor distancia
    most_similar_index = distance_matrix[i].argmin()
    # Obtener el 'tag' a partir del index y la 'distancia'
    phrase = df_combined.iloc[most_similar_index]['phrase']
    tag = df_combined.iloc[most_similar_index]['tag']
    score = distance_matrix[i][most_similar_index]
    tags.append(tag)
    scores.append(score)
    phrases.append(phrase)

# Guardamos los resultados en un dataframe
results_df = pd.DataFrame({
    'Bigram': bigrams_list,
    'Tag': tags,
    'Phrase': phrases,
    'Score': scores
})

# Ver resultados
results_df.sort_values(by='Score').head(100)
Bigram Tag Phrase Score
54862 oruga desgranadora plagas oruga desgranadora 0.000000
2264 zapallito amargo malezas zapallito amargo 0.000000
11054 analisis suelo analisis suelo analisis suelo 0.000000
23727 bowlesia incana malezas bowlesia incana 0.000000
944 escoba dura malezas escoba dura 0.000000
... ... ... ... ...
15657 yuyo colorafo malezas yuyo colorado 0.076923
9023 yuyi colorado malezas yuyo colorado 0.076923
46453 general lotes general lote general lote 0.076923
38651 isoca medidor plagas isoca medidora 0.076923
40947 isocas espiga plagas isoca espiga 0.076923

100 rows × 4 columns

results_df_2 = results_df.copy()
results_df_2["Score"] = round(results_df_2["Score"],2)
results_df_2.columns = ["Bigram","Phrase","Tag","Score"]
results_df_2.sort_values(by='Score').head(300).sample(15, random_state=15).sort_values("Score")
Bigram Phrase Tag Score
38007 karate zeon insecticida karate zeon 0.00
49826 royas anaranjada enfermedades roya anaranjada 0.06
51207 choris virgata malezas chloris virgata 0.07
3374 rama negras malezas rama negra 0.09
19489 mami guacho malezas mani guacho 0.09
15905 rama negrs malezas rama negra 0.10
10430 lecheron chamico malezas lecheron chico 0.12
13864 bolsita pastor malezas bolsa pastor 0.14
43126 pastoreo colorado malezas pasto colorado 0.18
923 maiz wacho malezas maiz guacho 0.20
10644 x borreria malezas borreria 0.20
22206 seca mani malezas sacha mani 0.22
40135 commelina morenita malezas commelina erecta 0.22
52976 dula gold herbicida dual gold 0.22
24885 enfermedad ve enfermedades enfermedades 0.23
# distribución Score
results_df["Score"].describe()
count    59552.000000
mean         0.547778
std          0.087395
min          0.000000
25%          0.500000
50%          0.562500
75%          0.600000
max          1.000000
Name: Score, dtype: float64
results_df["Score"].hist()
<Axes: >
# filtrando scores menores a 0.4
score_max = 0.42
results_df_filtrado = results_df[results_df["Score"]<score_max].reset_index(drop=True)
results_df_filtrado.sort_values(by='Score')
Bigram Tag Phrase Score
0 rama negra malezas rama negra 0.000000
133 escoba dura malezas escoba dura 0.000000
4442 sonchus oleracea malezas sonchus oleracea 0.000000
873 gusano cogollero plagas gusano cogollero 0.000000
1001 bolsa pastor malezas bolsa pastor 0.000000
... ... ... ... ...
1011 cargo zazola malezas carbon panoja 0.416667
4781 norte espiga enfermedades carbon espiga 0.416667
2259 dicamba pano herbicida dicamba 0.416667
989 bueno planta stand plantas stand plantas 0.416667
716 cosecha soja cosecha cosecha 0.416667

5194 rows × 4 columns

# Guardamos resultado Levenshtein para etiquetar
if SAVE_NOTEBOOK_RESULTS:
    results_df_filtrado.to_csv("Workflow_output/results_df_filtrado.csv", index=False)
# Guardamos resultado Levenshtein sin punto de corte (sin filtrar)
if SAVE_NOTEBOOK_RESULTS:
    results_df.to_csv("Workflow_output/results_df.csv", index=False)

Asignar 'tag' sobre el dataset final

# definimos función para matchear los tags de los bigramas con los comentarios
def find_all_tags(comment, phrase_to_tag):
    return [tag for phrase, tag in phrase_to_tag.items() if phrase in comment.lower()]

# aplicamos
# crear un diccionario de bigramas y phrases
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Phrase']))
annotations['comment_phrases'] = annotations['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# crear un diccionario de bigramas y scores
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Score']))
annotations['comment_scores'] = annotations['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# crear un diccionario de bigramas y tags
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Tag']))
annotations['comment_tags'] = annotations['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# Función para obtener el tag o score con el mayor valor (o 'sin etiquetar' si la lista está vacía)
def get_max_value(row, value_type='tag'):
    if row['comment_scores']:  # Verificamos si la lista no está vacía
        max_score_index = row['comment_scores'].index(min(row['comment_scores']))  # O min si quieres el valor más bajo
        return row['comment_tags'][max_score_index] if value_type == 'tag' else row['comment_scores'][max_score_index]
    else:
        return 'sin etiquetar'  # Devuelve 'sin etiquetar' si la lista de scores está vacía

# Aplicamos la función al DataFrame para tags
annotations['best_tag'] = annotations.apply(lambda row: get_max_value(row, 'tag'), axis=1)

# Aplicamos la función al DataFrame para scores
annotations['best_score'] = annotations.apply(lambda row: get_max_value(row, 'score'), axis=1)
# distribución de los tags imputados a partir de los comentarios
annotations['best_tag'].value_counts(dropna=False, normalize=True).head(6).round(2)
best_tag
sin etiquetar    0.57
malezas          0.22
general lote     0.05
plagas           0.04
enfermedades     0.03
fungicida        0.02
Name: proportion, dtype: float64
annotations#[annotations['best_tag']=="plagas"]
annotation_id company_id season_id property_id area_id comments scouter_id annotation_date annotation_tags annotation_longitude annotation_latitude comments_clean len_comments_clean comments_clean_2 len_comments_clean_2 comment_phrases comment_scores comment_tags best_tag best_score
0 01ff3c51-d6c2-4b9a-9876-409e75a824d5 0afb4f42-d63b-45ee-bbf8-df16f94529f7 289c09d7-ce26-4fa9-bf9a-b7cab391898f bd3e8b1b-6914-4e22-9e63-1c0c66ccb148 91420df8-61ba-11ed-9c62-e1a227e78cde Ir hablado con desyuyadores ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-07 13:31:09.234 NaN -62.578112 -33.103599 ir hablado con desyuyadores 27 ir hablado con desyuyadores 27 [] [] [] sin etiquetar sin etiquetar
1 404e3e04-b713-493e-830a-56570f997a14 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 036afeae-1c42-4c93-9a06-2f20977b89ac Maiz recien sembrado . Todo ok ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 10:30:55.251 NaN -62.809891 -33.741697 maiz recien sembrado todo ok 28 maiz recien sembrado ok 23 [] [] [] sin etiquetar sin etiquetar
2 60f06b01-c926-42b3-b79d-5b4b61e2e6fb 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae b24c5fd5-87b2-40dd-97cc-ca4edff7a625 Comienzo siembra maiz tardio. Excelente barbecho ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 10:30:05.531 NaN -62.809891 -33.741697 comienzo siembra maiz tardio excelente barbecho 47 comienzo siembra maiz tardio excelente barbecho 47 [calidad siembra] [0.375] [calidad siembra] calidad siembra 0.375
3 b53c4cac-1a21-45fe-be7c-235b08d704c9 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 1af5d0f7-7a22-4aef-8c10-c65c12708ab3 Hacer preemergente con glifo y acuron ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 12:18:16.170 NaN -62.770486 -33.655338 hacer preemergente con glifo y acuron 37 hacer preemergente con glifo acuron 35 [] [] [] sin etiquetar sin etiquetar
4 6bbdc37c-4ca4-4082-a443-aedd206cf24e 0afb4f42-d63b-45ee-bbf8-df16f94529f7 dcbb35dc-a961-4654-82f4-cb84e8e95329 07dc5555-019b-44c3-8045-ce127632a1ae 9d6caf86-eeca-4124-a2fb-42394a148771 Hacer preemergente con acuron y heat ac8b985a-2f62-4dab-9a57-bad510df7204 2022-12-14 12:17:43.856 NaN -62.770486 -33.655338 hacer preemergente con acuron y heat 36 hacer preemergente con acuron heat 34 [] [] [] sin etiquetar sin etiquetar
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
19728 0572c958-07e6-427b-9754-098a14a148c9 33f2dcae-049a-4b93-83f3-a8014ea7f0a0 c8a8c727-206e-46b6-a206-114aa68e824d 0f806757-e61d-4f67-81d0-0e71d97cb668 08860540-d869-11ed-98c6-150405b698ac lote muy limpio y muy bien planteado sobre soj... 2302d65d-52d4-4633-82b1-60523803d468 2024-08-27 18:15:41.000 NaN -59.081522 -37.594106 lote muy limpio y muy bien planteado sobre soj... 52 lote limpio bien planteado soja 31 [] [] [] sin etiquetar sin etiquetar
19729 ce32d686-b5ad-40ea-9d8e-bf3f6666db07 e88eecd6-d4f5-4b65-b9f2-87fc4b8de35c 7748349f-546c-4347-a194-82ec1aaa7173 42c22adc-1fa6-4c56-a594-f41120f6f77f 33335951-fa9f-4aad-9cd4-abab2869f225 Muy lindo esta el trigo.\r\nPulgon muy poco se... 03401ba8-df2d-43f1-a386-06f7a28e0ca3 2024-08-30 14:07:23.000 tags_general_disease,tags_general_pest -63.748140 -30.023821 muy lindo esta el trigo pulgon muy poco se ve ... 75 lindo trigo pulgon ve roya ve llegando hb 41 [] [] [] sin etiquetar sin etiquetar
19730 4e72bf13-5f07-431f-9188-1526dbc76901 6db7039e-7ffe-4f7b-a395-2d599879680b 261b3a42-5912-46d9-ba0e-48cd8a2fa7e3 e6b344e5-63d1-4ee7-b970-e7477c2342f6 c1134ad0-e89f-4e91-96c3-28bd326d0086 rastrojo limpio malezas 7c522b74-8858-4bae-9e34-aa715d329f32 2024-08-29 19:19:44.000 NaN -60.466058 -34.635506 rastrojo limpio malezas 23 rastrojo limpio malezas 23 [] [] [] sin etiquetar sin etiquetar
19731 604d7b7e-92c4-4307-89a8-6e91da2be83c 6db7039e-7ffe-4f7b-a395-2d599879680b 261b3a42-5912-46d9-ba0e-48cd8a2fa7e3 e6b344e5-63d1-4ee7-b970-e7477c2342f6 d2a4cc82-d4a5-4a1c-bea3-0a1094555766 efecto herbicida en malezas 7c522b74-8858-4bae-9e34-aa715d329f32 2024-08-29 19:20:41.000 NaN -60.466058 -34.635506 efecto herbicida en malezas 27 efecto herbicida malezas 24 [antidoto herbicida] [0.375] [antidoto herbicida] antidoto herbicida 0.375
19732 3c3ef340-7c87-43e4-b76b-814ad1143ea0 6db7039e-7ffe-4f7b-a395-2d599879680b 261b3a42-5912-46d9-ba0e-48cd8a2fa7e3 e6b344e5-63d1-4ee7-b970-e7477c2342f6 1349377d-0e73-42f7-a708-03daf9d4f735 efecto herbicida en malezas 7c522b74-8858-4bae-9e34-aa715d329f32 2024-08-29 19:20:15.000 NaN -60.466058 -34.635506 efecto herbicida en malezas 27 efecto herbicida malezas 24 [antidoto herbicida] [0.375] [antidoto herbicida] antidoto herbicida 0.375

19733 rows × 20 columns

# Guardamos base para modelo
if SAVE_NOTEBOOK_RESULTS:
    annotations.to_csv(file_with_tags, index=False)

Modelos de Clasificación

annotations = pd.read_csv(file_with_tags)
# preparar base
condicion = (annotations["best_tag"].isin(["malezas","enfermedades","plagas","fungicida","general lote"]))
campos_interes = ["comments_clean_2", "best_tag"]#, "comments_clean","comments_clean_2","best_score"]
df_model = annotations[condicion][campos_interes].reset_index(drop=True)
df_model
comments_clean_2 best_tag
0 excelente control malezas malezas
1 ojo seguir tema bolillera ipro plagas
2 ruedas cebollin con control parcial monitoreo ... malezas
3 maiz guacho naciendo sorgo halepo baja intensidad malezas
4 no ven nuevas infecciones enfermedades fungica... plagas
... ... ...
5908 cerraja mostacilla maiz guacho cebadilla basta... malezas
5909 cardos nabos capiqui rama negra ir con metribu... malezas
5910 manchones chicos avena guacha malezas
5911 manchones avena negra lamentablemente sector p... malezas
5912 avena negra ir con axial junto con poste malezas

5913 rows × 2 columns

import nltk
from nltk.classify import NaiveBayesClassifier, MaxentClassifier, SklearnClassifier
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import classification_report, accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer

X = df_model['comments_clean_2']
y = df_model['best_tag']

# Dividir en conjunto de entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Vectorización de texto con TF-IDF
vectorizer = TfidfVectorizer(max_features=5000)
X_train_tfidf = vectorizer.fit_transform(X_train).toarray()
X_test_tfidf = vectorizer.transform(X_test).toarray()

# Crear pares de características y etiquetas para nltk
train_data = [(dict(enumerate(x)), label) for x, label in zip(X_train_tfidf, y_train)]
test_data = [(dict(enumerate(x)), label) for x, label in zip(X_test_tfidf, y_test)]

# Naive Bayes Classifier
print("Naive Bayes Classifier")
nb_classifier = NaiveBayesClassifier.train(train_data)
nb_accuracy = nltk.classify.accuracy(nb_classifier, test_data)
print("Naive Bayes Accuracy:", nb_accuracy)

# Maxent Classifier (requiere el algoritmo GIS)
print("Maxent Classifier")
maxent_classifier = MaxentClassifier.train(train_data, algorithm='gis', max_iter=4)#, trace=0, max_iter=3, min_lldelta=0.5)#, algorithm='gis', max_iter=10)
maxent_accuracy = nltk.classify.accuracy(maxent_classifier, test_data)
print("Maxent Classifier Accuracy:", maxent_accuracy)

# SklearnClassifier con Logistic Regression
#print("SklearnClassifier")
sklearn_classifier = SklearnClassifier(MultinomialNB()).train(train_data)
sklearn_accuracy = nltk.classify.accuracy(sklearn_classifier, test_data)
print("MultinomialNB (SklearnClassifier) Accuracy:", sklearn_accuracy)
Naive Bayes Classifier
Naive Bayes Accuracy: 0.6534234995773457
Maxent Classifier
  ==> Training (4 iterations)

      Iteration    Log Likelihood    Accuracy
      ---------------------------------------
             1          -1.60944        0.124
             2          -0.83047        0.630
             3          -0.82752        0.630
         Final          -0.82538        0.630
Maxent Classifier Accuracy: 0.6348267117497887
MultinomialNB (SklearnClassifier) Accuracy: 0.7743026204564666
# Generar reporte detallado
nb_classifier_model = nb_classifier  # puedes elegir el mejor clasificador
y_pred_nb_classifier = [nb_classifier_model.classify(dict(enumerate(vectorizer.transform([comment]).toarray()[0]))) for comment in X_test]

maxent_classifier_model = maxent_classifier  # puedes elegir el mejor clasificador
y_pred_maxent_classifier = [maxent_classifier_model.classify(dict(enumerate(vectorizer.transform([comment]).toarray()[0]))) for comment in X_test]


sklearn_classifier_model = sklearn_classifier  # puedes elegir el mejor clasificador
y_pred_sklearn_classifier = [sklearn_classifier_model.classify(dict(enumerate(vectorizer.transform([comment]).toarray()[0]))) for comment in X_test]
df_test = pd.DataFrame({"y_test":y_test,"y_pred_nb_classifier":y_pred_nb_classifier,"y_pred_maxent_classifier":y_pred_maxent_classifier, "y_pred_sklearn_classifier":y_pred_sklearn_classifier})
df_test
y_test y_pred_nb_classifier y_pred_maxent_classifier y_pred_sklearn_classifier
5579 malezas malezas malezas malezas
3443 malezas malezas malezas malezas
2351 malezas malezas malezas malezas
4456 enfermedades malezas malezas general lote
2742 malezas malezas malezas malezas
... ... ... ... ...
1869 plagas malezas malezas enfermedades
2025 enfermedades malezas malezas malezas
5788 malezas malezas malezas malezas
653 malezas malezas malezas malezas
429 malezas malezas malezas malezas

1183 rows × 4 columns

print(df_test["y_test"].value_counts())
print(df_test["y_pred_nb_classifier"].value_counts())
print(df_test["y_pred_maxent_classifier"].value_counts())
print(df_test["y_pred_sklearn_classifier"].value_counts())
y_test
malezas         751
plagas          151
enfermedades    119
general lote    111
fungicida        51
Name: count, dtype: int64
y_pred_nb_classifier
malezas         1043
plagas            73
general lote      54
enfermedades      12
fungicida          1
Name: count, dtype: int64
y_pred_maxent_classifier
malezas    1183
Name: count, dtype: int64
y_pred_sklearn_classifier
malezas         963
plagas           94
general lote     63
enfermedades     54
fungicida         9
Name: count, dtype: int64

MaxentClassifier clasifica todos los comentarios como 'malezas'.

#df_test[df_test["y_pred_sklearn_classifier"]=="enfermedades"]

MaxentClassifier

print("\nClassification Report:\n", classification_report(df_test["y_test"], df_test["y_pred_maxent_classifier"]))
Classification Report:
               precision    recall  f1-score   support

enfermedades       0.00      0.00      0.00       119
   fungicida       0.00      0.00      0.00        51
general lote       0.00      0.00      0.00       111
     malezas       0.63      1.00      0.78       751
      plagas       0.00      0.00      0.00       151

    accuracy                           0.63      1183
   macro avg       0.13      0.20      0.16      1183
weighted avg       0.40      0.63      0.49      1183

NBClassifier

print("\nClassification Report:\n", classification_report(df_test["y_test"], df_test["y_pred_nb_classifier"]))
Classification Report:
               precision    recall  f1-score   support

enfermedades       0.83      0.08      0.15       119
   fungicida       1.00      0.02      0.04        51
general lote       0.59      0.29      0.39       111
     malezas       0.68      0.94      0.79       751
      plagas       0.33      0.16      0.21       151

    accuracy                           0.65      1183
   macro avg       0.69      0.30      0.32      1183
weighted avg       0.65      0.65      0.58      1183

Sklearn Classifier

print("\nClassification Report:\n", classification_report(df_test["y_test"], df_test["y_pred_sklearn_classifier"]))
Classification Report:
               precision    recall  f1-score   support

enfermedades       0.87      0.39      0.54       119
   fungicida       1.00      0.18      0.30        51
general lote       0.75      0.42      0.54       111
     malezas       0.77      0.99      0.86       751
      plagas       0.77      0.48      0.59       151

    accuracy                           0.77      1183
   macro avg       0.83      0.49      0.57      1183
weighted avg       0.79      0.77      0.74      1183

Validación

df_validation = pd.read_csv('../data/validation.csv', encoding='ISO-8859-1')
# limpieza
df_validation.loc[df_validation["specialist_tag"]=="maleza","specialist_tag"] = "malezas"
df_validation.loc[df_validation["specialist_tag"]=="plaga","specialist_tag"] = "plagas"
df_validation = df_validation[~df_validation["specialist_tag"].isna()].copy().reset_index(drop=True)

# filtramos etiquetas de interes
df_validation_filtrada = df_validation[df_validation["specialist_tag"].isin(['malezas','plagas','enfermedades','herbicida','fungicida','insecticida'])].copy().reset_index(drop=True)
df_validation_filtrada.head()
annotation_id company_id season_id property_id area_id comments specialist_tag scouter_id annotation_date annotation_tags annotation_longitude annotation_latitude comments_clean_2 comment_phrases comment_scores comment_tags best_tag best_score y_pred_sklearn_classifier
0 0c228426-d0a5-4c1c-b4d2-5729053d737e 1e09a723-b78a-405a-ba7d-4bb85d41fe3b 3aaf737d-241f-4376-ac44-317e455a65ca 1d4db68f-61f3-49a8-b9c9-95818da5781b 8bd5665c-a6e0-4eff-a07c-7e66e5947d07 rodales de rizoctonia enfermedades b434f734-51cf-47f0-bcb1-15d1de934b87 8/15/2023 tags_general_disease -61.262533 -38.824346 rodales rizoctonia [] [] [] sin etiquetar sin etiquetar malezas
1 6fafefea-5341-44ef-83ea-0c9ec4a39551 55ec9c39-a240-4db4-91f1-4d4cfa961250 3d7313f6-7753-40b8-b0d5-314700848c02 a380fba2-6d6d-4ea8-b257-62a1b626452f fc011c87-24f9-46d8-b54a-168b80818b11 Trigo MS 119 y MS 221 por el momento se compor... enfermedades f6160633-bee5-4881-9038-1576bf3ed8e7 8/8/2023 tags_general_disease -61.963047 -32.749646 trigo ms ms momento comporta bien roya anaranjada [roya anaranjada] [0.0] [enfermedades] enfermedades 0.0 enfermedades
2 47d21463-875a-4270-8a65-f908d5246518 2ac77d56-6fc5-4e28-be3f-9e3147fadcee d0925a06-8b39-4ca1-9b5b-04a54e3f1676 b769fa29-bfa2-4598-b184-53570f447e02 dfe6e756-68df-40b8-aad6-4298e98274da Roya Amarilla controlada enfermedades 36568ca0-67ca-49ac-a44b-084e52d2c584 8/28/2023 tags_general_disease -63.419535 -31.155053 roya amarilla controlada [roya asiatica] [0.38461538461538464] [enfermedades] enfermedades 0.384615 enfermedades
3 82a64138-1be2-481b-a96f-aee4e4635c51 e8b4d4a7-825e-49e4-94c5-5730fa5ad33e db99aee4-7ae9-42d7-a4b5-31d2abc81e86 6cb5fc79-5d9b-485b-a6a1-b3f5721de67a d39afe28-387a-411e-a3a6-9205177228a7 Bastante roya amarilla en las hojas inferiores... enfermedades 54cd9c8c-0799-449c-9a22-d9f75c5dd173 9/5/2023 tags_general_disease -61.542116 -38.725714 bastante roya amarilla hojas inferiores vamo a... [roya asiatica] [0.38461538461538464] [enfermedades] enfermedades 0.384615 enfermedades
4 84d6daf6-f51e-4444-bbb0-addc3b1fe07f 0ba19ff1-3805-4f5b-81c1-fa46a617f6f9 8afa750f-5296-416a-a7e7-db691b0af69e 2aca3eb4-1bd3-4d38-8912-9d6aba28a3e7 fb41822c-be1c-48e2-89ea-abb993b2172c estriado mas pustulas enfermedades f60a01a5-4145-4343-9d71-026617c8cb6a 9/18/2023 tags_general_disease -61.292842 -38.825694 estriado pustulas [] [] [] sin etiquetar sin etiquetar enfermedades
df_validation_filtrada.specialist_tag.value_counts()
specialist_tag
malezas         437
enfermedades     55
plagas           51
herbicida        26
insecticida       2
Name: count, dtype: int64
# aplicamos modelo vs clasificador
# procesamiento
df_validation_filtrada['comments_clean_2'] = df_validation_filtrada['comments'].apply(limpiar_texto)
# aplicamos clasificador con diccionario
# crear un diccionario de bigramas y phrases
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Phrase']))
df_validation_filtrada['comment_phrases'] = df_validation_filtrada['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# crear un diccionario de bigramas y scores
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Score']))
df_validation_filtrada['comment_scores'] = df_validation_filtrada['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# crear un diccionario de bigramas y tags
phrase_to_tag = dict(zip(results_df_filtrado['Bigram'].str.lower(), results_df_filtrado['Tag']))
df_validation_filtrada['comment_tags'] = df_validation_filtrada['comments_clean_2'].apply(lambda comment: find_all_tags(comment, phrase_to_tag))

# Aplicamos la función al DataFrame para tags
df_validation_filtrada['best_tag'] = df_validation_filtrada.apply(lambda row: get_max_value(row, 'tag'), axis=1)

# Aplicamos la función al DataFrame para scores
df_validation_filtrada['best_score'] = df_validation_filtrada.apply(lambda row: get_max_value(row, 'score'), axis=1)
# aplicamos modelo
y_pred_sklearn_classifier = [sklearn_classifier_model.classify(dict(enumerate(vectorizer.transform([comment]).toarray()[0]))) for comment in df_validation_filtrada['comments_clean_2']]
df_validation_filtrada["y_pred_sklearn_classifier"] = y_pred_sklearn_classifier
print("\nClassification Report:\n", classification_report(df_validation_filtrada["specialist_tag"], df_validation_filtrada["best_tag"]))
Classification Report:
                   precision    recall  f1-score   support

 calidad siembra       0.00      0.00      0.00         0
    enfermedades       0.88      0.25      0.39        55
       fungicida       0.00      0.00      0.00         0
    general lote       0.00      0.00      0.00         0
       herbicida       1.00      0.35      0.51        26
     insecticida       0.00      0.00      0.00         2
   malas hierbas       0.00      0.00      0.00         0
         malezas       0.99      0.42      0.59       437
          plagas       0.87      0.25      0.39        51
plantas daninhas       0.00      0.00      0.00         0
   sin etiquetar       0.00      0.00      0.00         0
   stand plantas       0.00      0.00      0.00         0
       unkrauter       0.00      0.00      0.00         0

        accuracy                           0.38       571
       macro avg       0.29      0.10      0.15       571
    weighted avg       0.96      0.38      0.55       571

Clasificación con diccionario y distancia de Levenshtein - F1 Score:

  • enfermedades: 0.39
  • herbicida: 0.51
  • insecticida: 0.00
  • malezas: 0.59
  • plagas: 0.39
print("\nClassification Report:\n", classification_report(df_validation_filtrada["specialist_tag"], df_validation_filtrada["y_pred_sklearn_classifier"]))
Classification Report:
               precision    recall  f1-score   support

enfermedades       1.00      0.62      0.76        55
   herbicida       0.00      0.00      0.00        26
 insecticida       0.00      0.00      0.00         2
     malezas       0.85      1.00      0.92       437
      plagas       0.95      0.37      0.54        51

    accuracy                           0.86       571
   macro avg       0.56      0.40      0.44       571
weighted avg       0.83      0.86      0.82       571

Sklearn Classifier - F1 Score:

  • enfermedades: 0.76
  • herbicida: 0.00
  • insecticida: 0.00
  • malezas: 0.92
  • plagas: 0.54